
'''
    Software License

    Copyright (C) 2021-05-24  Xoronos

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 3.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
'''

'''
    Liabilities

    The software is provided "AS IS" without any warranty of any kind, either expressed,
    implied, or statutory, including, but not limited to, any warranty that the software
    will conform to specifications, any implied warranties of merchantability, fitness
    for a particular purpose, and freedom from infringement, and any warranty that the
    documentation will conform to the software, or any warranty that the software will
    be error free.

    In no event shall Xoronos be liable for any damages, including, but not limited to,
    direct, indirect, special or consequential damages, arising out of, resulting from,
    or in any way connected with this software, whether or not based upon warranty,
    contract, tort, or otherwise, whether or not injury was sustained by persons or
    property or otherwise, and whether or not loss was sustained from, or arose out of
    the results of, or use of, the software or services provided hereunder.
    
    To request the provided software under a different license you can contact us at
    support@xoronos.com
'''

import os
import sys
import json


def is_valid_json_file(filename):
    # Check if the file exists
    if not os.path.isfile(filename):
        print(f"Error: The file {filename} does not exist.")
        return False
    
    # Check if the file content is valid JSON
    try:
        with open(filename, 'r') as file:
            json.load(file)
    except json.JSONDecodeError:
        print(f"Error: The file {filename} is not a valid JSON file.")
        return False

    return True

def file_to_expected_name( file_str ):
    if ( "xmatrix_comp" in file_str ) :
        expected_res = "matrix_compressed"
    elif ( "xmatrix" in file_str ) :
        expected_res = "matrix"
    elif ( "start_point" in file_str ) :
        expected_res = "start"
    elif ( "point" in file_str ) :
        expected_res = "point"
    elif ( "binomial_key" in file_str ) :
        expected_res = "binomial_key"
    elif ( "monomial_key" in file_str ) :
        expected_res = "monomial_key"
    elif ( "monomial_commitment" in file_str ) :
        expected_res = "monomial_commitment"
    elif ( "binomial_commitment" in file_str ) :
        expected_res = "binomial_commitment"
    elif ( "monomial_response" in file_str ) :
        expected_res = "monomial_response"
    elif ( "monomial_proof" in file_str ) :
        expected_res = "monomial_proof"
    elif ( "monovalent_key" in file_str ) :
        expected_res = "monovalent_key"
    elif ( "polyvalent_proof" in file_str ) :
        expected_res = "polyvalent_proof"
    elif ( "polyvalent_key" in file_str ) :
        expected_res = "polyvalent_key"
    elif ( "sym_signature" in file_str ) :
        expected_res = "signature_sym"
    elif ( "asy_signature" in file_str ) :
        expected_res = "signature_asy"
    elif ( "ring_signature" in file_str ) :
        expected_res = "ring_signature"
    elif ( "hash" in file_str ) :
        expected_res = "hash"
    elif ( "sym_certificate" in file_str ) :
        expected_res = "certificate_sym"
    elif ( "asy_certificate" in file_str ) :
        expected_res = "certificate_asy"
    else :
        expected_res = ""
    return expected_res

#########
# lexer #
#########

tool_name_str = "xrnarchive-cli"
cmdgenerate_client_script = "--generate-client-scripts"
optjson_command = "-json-command"
optinput_copy_archive = "-copy-inputs-and-archive-script"
cmdcheck_input = "--check-inputs"
cmdcheck_output = "--check-outputs"
optcopy_output_script = "-extract-outputs-and-copy-script"
cmdgenerate_server_script = "--generate-server-scripts"
optcopy_outputs_and_archive = "-archive-outputs-script"
optlogfile = "-json-log"

def print_help():
    print("\n# help page for the {} command\n".format(tool_name_str))
    print("# to create client scripts")
    print("{} {} \\".format(tool_name_str,cmdgenerate_client_script))
    print("    {} command.json \\".format(optjson_command ))
    print("    {} copy_and_archive.sh \\".format(optinput_copy_archive ))
    print("    {} exctract_and_copy_script.sh ".format(optcopy_output_script ))
    print("\n")
    print("./copy_and_archive.sh archive.tar /path/to/cache_dir ")
    print("./extract_and_copy_script.sh archive.tar /path/to/cache_dir ")
    print("")
    print("# to check the inputs or outputs files")
    print("{} {} {} command.json {} input_log.json ".format(tool_name_str,cmdcheck_input, optjson_command, optlogfile))
    print("{} {} {} command.json {} output_log.json ".format(tool_name_str,cmdcheck_output, optjson_command, optlogfile))
    print("\n# to create server scripts")
    print("{} {} \\".format(tool_name_str,cmdgenerate_server_script))
    print("    {} command.json \\".format(optjson_command ))
    print("    {} archive_outputs.sh ".format(optcopy_outputs_and_archive ))
    print("\n")
    print("./archive_outputs.sh archive.tar /path/to/cache_dir ")
    print("")

# check syntax
if (( len(sys.argv) != 8 ) and (  len(sys.argv) != 6  )) :
    print_help()
    exit(-1)

if ( (( len(sys.argv) == 8 ) and ( sys.argv[1] != cmdgenerate_client_script )) and ((  len(sys.argv) == 4  ) and ( sys.argv[1] != cmdcheck_input )) and ((  len(sys.argv) == 4  ) and ( sys.argv[1] != cmdcheck_output )) and (( len(sys.argv) == 6 ) and ( sys.argv[1] != cmdgenerate_server_script )) ) :
    print_help()
    exit(-1)

filejson_command = ""
fileinput_copy_archive = ""
filecopy_output_script = ""
fileserver = ""
filelog = ""

check_inputs = 0
check_outputs = 0

if ( sys.argv[1] == cmdgenerate_client_script ) :
    for i in range(1, 8):  
        if ( sys.argv[i] == optjson_command ) :
            filejson_command = sys.argv[i+1]
        if ( sys.argv[i] == optinput_copy_archive ) :
            fileinput_copy_archive = sys.argv[i+1]
        if ( sys.argv[i] == optcopy_output_script ) :
            filecopy_output_script = sys.argv[i+1]

    all_files = [ filejson_command , fileinput_copy_archive ,  filecopy_output_script ]

    for file in all_files :
        if ( file == "" ) :
            print_help()
            exit(-1)

elif (( sys.argv[1] == cmdcheck_input ) and (sys.argv[2] == optjson_command) and ( sys.argv[4] == optlogfile )) :
    if ( is_valid_json_file(sys.argv[3]) ):
        filejson_command = sys.argv[3]
        check_inputs = 1
    filelog = sys.argv[5]

elif (( sys.argv[1] == cmdcheck_input ) and (sys.argv[4] == optjson_command) and ( sys.argv[2] == optlogfile )) :
    if ( is_valid_json_file(sys.argv[5]) ):
        filejson_command = sys.argv[5]
        check_inputs = 1
    filelog = sys.argv[3]

elif (( sys.argv[1] == cmdcheck_output ) and (sys.argv[2] == optjson_command) and ( sys.argv[4] == optlogfile )) :
    if ( is_valid_json_file(sys.argv[3]) ):
        filejson_command = sys.argv[3]
        check_outputs = 1
    filelog = sys.argv[5]

elif (( sys.argv[1] == cmdcheck_output ) and (sys.argv[4] == optjson_command) and ( sys.argv[2] == optlogfile )) :
    if ( is_valid_json_file(sys.argv[5]) ):
        filejson_command = sys.argv[5]
        check_outputs = 1
    filelog = sys.argv[3]

elif (( sys.argv[1] == cmdgenerate_server_script ) and (sys.argv[2] == optjson_command) and (sys.argv[4] == optcopy_outputs_and_archive )) :
    if ( is_valid_json_file(sys.argv[3]) ):
        filejson_command = sys.argv[3]
        fileserver = sys.argv[5]

elif (( sys.argv[1] == cmdgenerate_server_script ) and (sys.argv[4] == optjson_command) and (sys.argv[2] == optcopy_outputs_and_archive )) :
    if ( is_valid_json_file(sys.argv[5]) ):
        filejson_command = sys.argv[5]
        fileserver = sys.argv[3]

else :
    print_help()
    exit(-1)



###################
# load json files #
###################

def strip_paths( command_json, tool_data_json ) :
    default_input_file = []
    default_output_file = []
    command_input_file = []
    command_output_file = []
    destination_command = command_json
    command_index = 0
    # change configuration files
    for configuration_command in command_json["configurations"] :
         for configuration_tool in tool_data_json["configurations"] :
             if ( configuration_command["name"] == configuration_tool["name"] ) : 
                 for option_tool in configuration_tool["options"] :
                     option_index = 0
                     for option_command in configuration_command["options"] :
                         if (( option_command["name"] == option_tool["name"] ) and ( option_command["value"] != None )) :
                             if ( option_tool["type"] == "file_read") :
                                 command_input_file.append( option_command["value"] )
                                 destination_command["configurations"][command_index]["options"][option_index]["value"] = os.path.basename(option_tool["default"])
                                 default_input_file.append( os.path.basename(option_tool["default"]))
                             if ( option_tool["type"] == "file_write" ) :
                                 command_output_file.append( option_command["value"] )
                                 destination_command["configurations"][command_index]["options"][option_index]["value"] = os.path.basename(option_tool["default"])
                                 default_output_file.append( os.path.basename(option_tool["default"]))
                         option_index = option_index + 1
         command_index = command_index + 1

    # change command files
    for configuration_tool in tool_data_json["commands"] :
        if ( command_json["command"][0]["name"] == configuration_tool["name"] ) : 
            for option_tool in configuration_tool["options"] :
                option_index = 0
                for option_command in command_json["command"][0]["options"] :
                    if (( option_command["name"] == option_tool["name"] ) and ( option_command["value"] != None )) :
                        if ( option_tool["type"] == "file_read") :
                            command_input_file.append( option_command["value"] )
                            destination_command["command"][0]["options"][option_index]["value"] = os.path.basename(option_tool["default"])
                            default_input_file.append( os.path.basename(option_tool["default"]))
                        if ( option_tool["type"] == "file_write" ) :
                            command_output_file.append( option_command["value"] )
                            destination_command["command"][0]["options"][option_index]["value"] = os.path.basename(option_tool["default"])
                            default_output_file.append( os.path.basename(option_tool["default"]))
                    option_index = option_index + 1

    return destination_command, default_input_file, default_output_file, command_input_file, command_output_file

if ( filejson_command != "" ) :

    if ( is_valid_json_file( filejson_command ) ):
        with open(filejson_command, 'r') as file:
            command_json_tmp = json.load(file)
    else :
        exit(-1)

    if (( command_json_tmp["tool"] != "xrnlib-cli" ) and ( command_json_tmp["tool"] != "xrnconv-cli" )) :
        print("wrong tool")
        exit(-1)

    tools_data = json.loads( toolsjsonstr )

    for tool in tools_data["tool_list"] :
        if ( tool["name"] == command_json_tmp["tool"] ) :
            tool_data_json = tool

    stripped_command_json, default_input_file, default_output_file, command_input_file, command_output_file = strip_paths( command_json_tmp, tool_data_json )

    with open(filejson_command, 'r') as file:
        command_json = json.load(file)

    if ( check_inputs == 1 ) :
        get_configuration = "decoding-conf"
        default_file_set = default_input_file
        command_file_set = command_input_file 
    if ( check_outputs == 1 ) :
        get_configuration = "encoding-conf"
        default_file_set = default_output_file
        command_file_set = command_output_file 

    ####################
    # check data types #
    ####################

    gotten_configuration = 0
    if (( check_inputs == 1 ) or ( check_outputs == 1 )) :
        for configuration in command_json["configurations"]:
            if ( configuration["name"] == get_configuration ) :
                 gotten_configuration = 1
                 build_cmd = "xrnlib-cli --decoding-conf "
                 # get decoding-conf
                 for option in configuration["options"] :
                     if option["value"] != None :
                         if option["value"] != "void" :
                             build_cmd = build_cmd + "-" + option["name"] + " " + option["value"] + " "
                         else :
                             build_cmd = build_cmd + "-" + option["name"] + " "
            if (( configuration["name"] == "permutation-conf" ) and ( gotten_configuration == 1 )):
                 for option in configuration["options"] :
                     if option["name"] == "arch" :
                         build_cmd = build_cmd + "--permutation-conf -arch " + option["value"] + " --logging-conf -force-write -lib-info-log /dev/stdout --print-header -xrn-file "

        if ( gotten_configuration == 0 ):
            build_response = '{ "tool": "xrnarchive-cli", "files": [ ] }'
            with open(filelog, 'w') as logfile:
                logfile.write(build_response)
            exit(-1)

        file_index = 0
        build_response = '{ "tool": "xrnarchive-cli", "files": [ '
        for file in default_file_set :
            expected_name = file_to_expected_name(file)
            if ( expected_name != "" ) :
                exec_command = build_cmd + command_input_file[file_index]
                json_response_str = os.popen( exec_command ).read()
                json_response = json.loads(json_response_str)
                build_response = build_response + '{ "name": "'+ file + '", "xrnfile": '
                if ( json_response["message_type"] == "data_structure" ) : 
                     if ( json_response["header"]["type"] != expected_name ) :
                         build_response = build_response + 'false, "error_message" : "' + json_response_str +'" } ] }'
                         with open(filelog, 'w') as logfile:
                             logfile.write(build_response)
                         exit(-1)
                     else :
                         build_response = build_response + 'true, "error_message" : "" },'
                elif ( json_response["message_type"] == "encoding_parameters" ) or ( json_response["message_type"] == "block" ):
                     build_response = build_response + 'true, "error_message" : "" },'
                else :
                     build_response = build_response + 'false, "error_message" : "' + json_response_str +'" } ] }'
                     with open(filelog, 'w') as logfile:
                         logfile.write(build_response)
                     exit(-1)

                file_index = file_index + 1

        build_response = build_response[:-1]
        if ( check_inputs == 1 ) :
            build_response = build_response + ' ] }'
            with open(filelog, 'w') as logfile:
                logfile.write(build_response)
        elif ( check_outputs == 1 ) :
            build_response = build_response + ' ] }'
            with open(filelog, 'w') as logfile:
                logfile.write(build_response)
        exit(0)

######################
# create copy script #
######################

if ( fileinput_copy_archive != "" ):
    with open(fileinput_copy_archive, 'w') as file:
        i = 0
        file.write("#!/bin/bash\n\n")
        file.write("if [ -z \"$1\" ]; then\n")
        file.write("    echo \"arguments not provided\"\n")
        file.write("    echo \"./"+fileinput_copy_archive+" archive.tar /path/to/cache\"\n")
        file.write("    exit -1\n")
        file.write("fi\n")
        file.write("if [ ! -d \"$2\" ]; then\n")
        file.write("    echo \"$2 is not a directory\"\n")
        file.write("    echo \"./"+fileinput_copy_archive+" archive.tar /path/to/cache\"\n")
        file.write("    exit -1\n")
        file.write("fi\n\n")
        for file_name in default_input_file :
            if os.path.isfile(command_input_file[i]):
                file.write("cp " + command_input_file[i] + " $2/" + file_name  +"\n")
                i = i + 1
            else :
                file.write("echo " + command_input_file[i] + " file does not exist;\n exit -1;\n")
        file.write("\necho \"" + str(stripped_command_json).replace("'","\\\"").replace("None","null") + " \" > $2/command.json\n\n")

        file.write("archive_name=$(basename \"$1\")\n")
        file.write("\ncd $2; tar -cvf $archive_name ./*;\nif [ $archive_name != $1 ]; then\n    mv $archive_name $1\nfi" )

if ( filecopy_output_script != "" ):
    with open(filecopy_output_script, 'w') as file:
        i = 0
        file.write("#!/bin/bash\n\n")
        file.write("if [ -z \"$1\" ]; then\n")
        file.write("    echo \"arguments not provided\"\n")
        file.write("    echo \"./"+filecopy_output_script+" archive.tar /path/to/cache\"\n")
        file.write("    exit -1\n")
        file.write("fi\n")
        file.write("if [ ! -d \"$2\" ]; then\n")
        file.write("    echo \"$2 is not a directory\"\n")
        file.write("    echo \"./"+filecopy_output_script+" archive.tar /path/to/cache\"\n")
        file.write("    exit -1\n")
        file.write("fi\n\n")
        file.write("tar -xf $1 -C $2\n\n")
        for file_name in command_output_file :
            file.write("if [ -f $2/" + default_output_file[i] + " ]; then\n")
            file.write("    cp $2/" + default_output_file[i] + " " + file_name +"\n")
            file.write("else\n")
            file.write("    echo \"$2/" + default_output_file[i] + " file does not exist\"\n    exit -1\n")
            file.write("fi\n\n")
            i = i + 1
        file.write("\n" )

if ( fileserver != "" ):
    with open(fileserver, 'w') as file:
        file.write("#!/bin/bash\n\n")
        file.write("if [ -z \"$1\" ]; then\n")
        file.write("    echo \"arguments not provided\"\n")
        file.write("    echo \"./"+fileserver+" archive.tar /path/to/cache\"\n")
        file.write("    exit -1\n")
        file.write("fi\n")
        file.write("if [ ! -d \"$2\" ]; then\n")
        file.write("    echo \"$2 is not a directory\"\n")
        file.write("    echo \"./"+fileserver+" archive.tar /path/to/cache\"\n")
        file.write("    exit -1\n")
        file.write("fi\n\n")
        for file_name in default_output_file :
            file.write("if [ ! -f $2/" + file_name + " ]; then\n")
            file.write("    echo \"$2/" + file_name + " file does not exist\"\n    exit -1\n")
            file.write("fi\n\n")

        file.write("tar -cf $1")
        for file_name in default_output_file :
            file.write(" $2/" + file_name )
        file.write("\n" )


